﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace Black.SurfaceToolkit.Controls
{
    /// <summary>
    /// Représente un <see cref="T:System.Windows.Controls.StackPanel" /> dont un élément occupe l'espace restant.
    /// </summary>
    /// <remarks>
    /// <para>Ce contrôle dispose ses éléments enfants comme un <see cref="T:System.Windows.Controls.StackPanel" /> à l'exception de l'enfant d'index <see cref="P:Black.SurfaceToolkit.Controls.FillStackPanel.FillItemIndex" /> qui occupe tout l'espace restant.</para>
    /// <para><div class="caption" style="font-weight : bold">FillStackPanel dont l'élément 2 est séléctionné</div></para>
    /// <para><img src="../Images/FillStackPanel.Selected.png" /></para>
    /// <para>Si <see cref="P:Black.SurfaceToolkit.Controls.FillStackPanel.FillItemIndex" /> est -1 (pas de séléction), ce contrôle ce comporte exactement comme un <see cref="T:System.Windows.Controls.StackPanel" />.</para>
    /// <para><div class="caption" style="font-weight : bold">FillStackPanel sans séléction</div></para>
    /// <para><img src="../Images/FillStackPanel.Unselected.png" /></para>
    /// </remarks>
    [Browsable(true), Description("Dispose les éléments pour remplir l'espace disponible")]
    public class FillStackPanel : StackPanel
    {
        #region Propriétées de dépendances

        /// <summary>
        /// Identifie le propriété de dépendance <see cref="P:Black.SurfaceToolkit.Controls.FillStackPanel.FillItemIndex" />.
        /// </summary>
        public static readonly DependencyProperty FillItemIndexProperty = DependencyProperty.Register("FillItemIndex", typeof(int), typeof(FillStackPanel), new FrameworkPropertyMetadata(-1, FrameworkPropertyMetadataOptions.AffectsMeasure));

        #endregion

        #region Accesseurs

        /// <summary>
        /// Obtient ou définit l'index de l'élément de remplissage.
        /// </summary>
        [Browsable(true), DefaultValue(-1), Description("Elément occupant la place restante"), Category("FillStackPanel")]
        public int FillItemIndex
        {
            get
            {
                return ((int)this.GetValue(FillStackPanel.FillItemIndexProperty));
            }
            set
            {
                this.SetValue(FillStackPanel.FillItemIndexProperty, value);
            }
        }

        #endregion

        #region Constructeurs

        /// <summary>
        /// Initialise une nouvelle instance de <see cref="T:Black.SurfaceToolkit.Controls.FillStackPanel" />.
        /// </summary>
        public FillStackPanel()
            : base()
        {
        }

        #endregion

        #region Fonctions protégées

        /// <summary>
        /// Réarrange l'élément et ses enfants.
        /// </summary>
        /// <param name="p_oArrangeSize">Taille maximum autorisée.</param>
        /// <returns>Taille utilisée.</returns>
        protected override Size ArrangeOverride(Size p_oArrangeSize)
        {
            UIElementCollection oInternalChildren = this.InternalChildren;
            bool bHorizontal = this.Orientation == Orientation.Horizontal;
            Rect oFinalRect = new Rect(p_oArrangeSize);
            int iCount = oInternalChildren.Count;

            int iFillItemIndex = this.FillItemIndex;

            if (iFillItemIndex < 0 || iFillItemIndex >= iCount)
                iFillItemIndex = iCount;

            // Elements précédents

            for (int i = 0; i < iFillItemIndex; i++)
            {
                UIElement oElement = oInternalChildren[i];

                if (oElement != null)
                {
                    if (bHorizontal)
                    {
                        oFinalRect.Width = oElement.DesiredSize.Width;
                        oFinalRect.Height = Math.Max(p_oArrangeSize.Height, oElement.DesiredSize.Height);
                    }
                    else
                    {
                        oFinalRect.Height = oElement.DesiredSize.Height;
                        oFinalRect.Width = Math.Max(p_oArrangeSize.Width, oElement.DesiredSize.Width);
                    }

                    oElement.Arrange(oFinalRect);

                    if (bHorizontal)
                        oFinalRect.X += oFinalRect.Width;
                    else
                        oFinalRect.Y += oFinalRect.Height;
                }
            }

            Rect oFillRect = oFinalRect;

            if (bHorizontal)
                oFinalRect.X = p_oArrangeSize.Width;
            else
                oFinalRect.Y = p_oArrangeSize.Height;

            oFinalRect.Size = new Size(0.0, 0.0);

            for (int i = iCount - 1; i > iFillItemIndex; i--)
            {
                UIElement oElement = oInternalChildren[i];

                if (oElement != null)
                {
                    if (bHorizontal)
                    {
                        oFinalRect.Width = oElement.DesiredSize.Width;
                        oFinalRect.Height = Math.Max(p_oArrangeSize.Height, oElement.DesiredSize.Height);

                        oFinalRect.X -= oFinalRect.Width;
                    }
                    else
                    {
                        oFinalRect.Height = oElement.DesiredSize.Height;
                        oFinalRect.Width = Math.Max(p_oArrangeSize.Width, oElement.DesiredSize.Width);

                        oFinalRect.Y -= oFinalRect.Height;
                    }

                    oElement.Arrange(oFinalRect);
                }
            }

            if (iFillItemIndex != iCount)
            {
                UIElement oElement = oInternalChildren[iFillItemIndex];

                if (bHorizontal)
                    oFillRect.Width = oFinalRect.X - oFillRect.X;
                else
                    oFillRect.Height = oFinalRect.Y - oFillRect.Y;

                oElement.Arrange(oFillRect);
            }

            return (p_oArrangeSize);
        }

        /// <summary>
        /// Mesure l'élément et ses enfants en prévision de leur réorganisation pendant la passe <see cref="M:Black.SurfaceToolkit.Controls.FillStackPanel.ArrangeOverride(System.Windows.Size)" />.
        /// </summary>
        /// <param name="p_oConstraint">Taille limite supérieure à ne pas dépasser.</param>
        /// <returns>Taille désirée de l'élément.</returns>
        protected override Size MeasureOverride(Size p_oConstraint)
        {
            Size oSize = new Size();

            UIElementCollection oInternalChildren = this.InternalChildren;
            Size oAvailableSize = p_oConstraint;
            bool bHorizontal = this.Orientation == Orientation.Horizontal;
            double dConstraintSize = bHorizontal ? p_oConstraint.Width : p_oConstraint.Height;
            int iCount = oInternalChildren.Count;

            int iFillItemIndex = this.FillItemIndex;

            for (int i = 0; i <= iCount; i++)
            {
                int iRealIndex = i;

                if (i == iCount)
                {
                    iRealIndex = iFillItemIndex;

                    if (iRealIndex < 0 || iRealIndex >= iCount)
                        break;
                }
                else
                {
                    if (i == iFillItemIndex)
                        continue;
                }

                UIElement oElement = oInternalChildren[iRealIndex];

                if (oElement != null)
                {
                    oElement.Measure(oAvailableSize);

                    Size desiredSize = oElement.DesiredSize;

                    if (bHorizontal)
                    {
                        oAvailableSize.Width -= desiredSize.Width;
                        oSize.Width += desiredSize.Width;
                        oSize.Height = Math.Max(oSize.Height, desiredSize.Height);
                    }
                    else
                    {
                        oAvailableSize.Height -= desiredSize.Height;
                        oSize.Height += desiredSize.Height;
                        oSize.Width = Math.Max(oSize.Width, desiredSize.Width);
                    }
                }
            }

            return (oSize);
        }

        #endregion
    }
}
